/**
 * This abstract class implements some of the basic functionality
 * of any matrix that can be universal regardless of the actual
 * representation.  To implement the functions given here, make use
 * of the other functions given in the interface; even though they
 * are not explicitly written in this file, any full implementation
 * of a matrix class will have them available (e.g. set, get). 
 * 
 * !!!!! Do not add any public or protected variables or methods
 * to this class or implement any other methods of the interface
 * other than the ones given.  You may add any PRIVATE variables
 * or methods as you see necessary, but do not add any additional
 * major data structures. !!!!!
 */


/*
 * Author: Yuan Li
 * Titile: 15121 summer 2011 lab3
 * Date: Aug 13 2011
 */
import java.util.Scanner;




public abstract class AbstractMatrix implements Matrix
{
	
	/* The dimensions of the matrix */
	private int rows, cols;
	
	public final int getRows()
	{
		return rows;
	}
	
	public final int getCols()
	{
		return cols;
	}
	
	public final void init(int rows, int cols)
	{
		this.rows = rows;
		this.cols = cols;
		clear();
	}
	
	// I add these abstract method in case forget to implement in the subclass
	public abstract void clear();
	
	public abstract void set(int row, int col, double val);
	
	public abstract double get(int row, int col);
	
	public abstract double[] getRow(int row);
	
	public abstract double[] getCol(int col);
	
	public abstract double rowSum(int row);
	
	public abstract double colSum(int col);
	
	public abstract boolean checkRepresentation();
	///////////////////////////////////////////////////////////////
	
	
	
	/**
	 * checkDimen -- will check whether current matrix has the same dimension with the given matrix
	 * @param m the given matrix
	 */
	private void checkDimen(Matrix m)
	{
		if ((this.getRows() != m.getRows()) && (this.getCols() != m.getCols())){
			throw new IllegalArgumentException("Two matrixes do not have equal dimension!!");
		}
		
	}
	
	public final void add(Matrix m)
	{
		// check the dimension //
		checkDimen(m);
		
		// add the elements one by one
		for (int row=0; row < this.getRows(); row++){
			for (int col=0; col < this.getCols(); col++){
				this.set(row, col, (this.get(row, col) + m.get(row, col)));
			}
		}
		
		return;
		//throw new RuntimeException("You must implement this method!");
	}
	
	public final void sub(Matrix m)
	{
		checkDimen(m);
		
		// sub the elements one by one
		for (int row=0; row < this.getRows(); row++){
			for (int col=0; col < this.getCols(); col++){
				this.set(row, col, this.get(row, col) + m.get(row, col));
			}
		}
		
		return;
	}
	
	public final boolean equals(Matrix m)
	{
		// check the dimension
		if ((this.getRows() != m.getRows()) && (this.getCols() != m.getCols())){
			return false;
		}
		
		// check every element in the matrix
		for (int row=0; row < this.getRows(); row++){
			for (int col=0; col < this.getCols(); col++){
				if (this.get(row, col) != m.get(row, col))
					return false;
			}
		}
		
		// everything seems ok, return true.
		return true;
		//throw new RuntimeException("You must implement this method!");
	}
	
	public final boolean isSymmetric()
	{
		//check whether the matrix is square 
		if (this.getRows() != this.getCols()){
			return false;
		}
		
		// check x[i,j] = x[j,i]
		for (int row=0; row < this.getRows(); row++){
			for (int col=0; col < this.getCols(); col++){
				if (this.get(row, col) != this.get(col, row));
					return false;
			}
		}
		
		return true;
		//throw new RuntimeException("You must implement this method!");
	}
	
	public final boolean importData(Scanner input)
	{
		try{
			// first read the dimension, rows, cols
			this.init(input.nextInt(), input.nextInt());
			while(input.hasNext()){
				this.set(input.nextInt(), input.nextInt(), input.nextDouble());
			}
			return true;
		}
		catch (Exception e){
			System.out.println("Error at importData: " + e);
			return false;
		}
		
		//throw new RuntimeException("You must implement this method!");
	}
	
	public final String toString()
	{
		StringBuffer sb = new StringBuffer();
		//print tile
		sb.append("    \t");
		for(int col=0; col < this.getCols(); col++){
			sb.append("(" + col + ")\t");
		}
		sb.append("\n");
		
		for (int row=0; row < this.getRows(); row++){
			sb.append("(" + row + "): [\t");
			for (int col=0; col < this.getCols(); col++){
				sb.append(this.get(row, col) + "\t");
			}
			sb.append("]\n");
		}
		return sb.toString();
		//throw new RuntimeException("You must implement this method!");
	}
	
}
